package com.csdj.server.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName: JwtTokenUtil
 * @Author: ZouLi
 * @Description: token 工具类
 * @Date: 2022/12/13 8:45
 **/
@Component
public class JwtTokenUtil {
    /**
     * 存放用户名
     **/
    private static final String CLAIM_KEY_USERNAME = "sub";
    /**
     * 存放创建时间
     **/
    private static final String CLAIM_KEY_CREATED = "created";

    /**
     * token秘钥
     **/
    @Value("${jwt.secret}")
    private String secret;

    /**
     * token有效时间
     **/
    @Value("${jwt.expiration}")
    private long expiration;

    // 第一部分 生成token
    /**
     * @Description: 准备消息主体
     * @Param [userDetails]
     * @Return java.lang.String
     * @Throws
     **/
    public String generateToken(UserDetails userDetails){
        Map<String, Object> claims = new HashMap<>();
        // 存放用户名
        claims.put(CLAIM_KEY_USERNAME,userDetails.getUsername());
        // 存放token创建时间
        claims.put(CLAIM_KEY_CREATED,new Date());
        return generateToken(claims);
    }

    /**
     * @Description: 根据消息载体生成token返回出去
     * @Param [claims]
     * @Return java.lang.String
     * @Throws
     **/
    private String generateToken(Map<String, Object> claims) {
        // 构建token
        return Jwts.builder()
                // 存放用户信息
                .setClaims(claims)
                // token失效时间设置
                .setExpiration(generateExpirationDate())
                // 设置秘钥
                .signWith(SignatureAlgorithm.HS512,secret)
                // 生成
                .compact();
    }

    /**
     * @Description: 生成token的有效时间
     * @Param []
     * @Return java.util.Date
     * @Throws
     **/
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis()+expiration*1000);
    }

    // 第二部分 解析token
    /**
     * @Description: 根据token获取用户信息载体
     * @Param [token]
     * @Return io.jsonwebtoken.Claims
     * @Throws
     **/
    public Claims getTokenInfo(String token){
        Claims claims = null;
        try{
            claims=Jwts.parser()
                    // 设置秘钥
                    .setSigningKey(secret)
                    // 设置token
                    .parseClaimsJws(token)
                    // 获取用户主体
                    .getBody();
        } catch(Exception e){
            e.printStackTrace();
        }
        return claims;
    }

    /**
     * @Description: 根据用户信息载体获取用户名
     * @Param [token]
     * @Return java.lang.String
     * @Throws
     **/
    public String getUsernameFormClaims(String token){
        String username = null;
        try{
            Claims claims = getTokenInfo(token);
            username = claims.getSubject();
        } catch(Exception e){
            e.printStackTrace();
        }
        return username;
    }

    /**
     * @Description: 根据用户信息载体获取token有效时间
     * @Param [token]
     * @Return java.util.Date
     * @Throws
     **/
    public Date getTokenExpirationDate(String token){
        Claims claims = getTokenInfo(token);
        return claims.getExpiration();
    }

    // 第三部分 判断token有效性
    /**
     * @Description: 判断token是否过期
     * @Param [token]
     * @Return boolean
     * @Throws
     **/
    private boolean isTokenExpired(String token){
        Date expirationDate = getTokenExpirationDate(token);
        return expirationDate.before(new Date());
    }

    /**
     * @Description: 判断token和用户信息是否同时有效
     * @Param [token, userDetails]
     * @Return boolean
     * @Throws
     **/
    public boolean validateToken(String token,UserDetails userDetails){
        String username = getUsernameFormClaims(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }

    /**
     * @Description: 判断token是否能刷新
     * @Param [token]
     * @Return boolean
     * @Throws
     **/
    public boolean isCanRefreshToken(String token){
        return !isTokenExpired(token);
    }

    /**
     * @Description: 刷新token
     * @Param [token]
     * @Return java.lang.String
     * @Throws
     **/
    public String refreshToken(String token){
        Claims claims = getTokenInfo(token);
        claims.put(CLAIM_KEY_CREATED,new Date());
        return generateToken(claims);
    }
}
